package com.codeczx.myretrofitdemo.retrofit;

import android.support.annotation.Nullable;

import java.io.IOException;

import okhttp3.*;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;

/**
 * Created by codeczx on 2018/3/28.
 */

public class OkHttpCall<T> implements Call<T> {

    private final ServiceMethod<T, ?> serviceMethod;
    private final Object[] args;

    public OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
    }

    @Override
    public Response<T> execute() {
        okhttp3.Call call;
        synchronized (this) {
            call = createRawCall();
        }
        try {

            return parseResponse(call.execute());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private okhttp3.Call createRawCall() {
        okhttp3.Request request = serviceMethod.toRequest();
        okhttp3.Call call = serviceMethod.callFactory.newCall(request);
        return call;
    }

    private Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        rawResponse = rawResponse.newBuilder()
                .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
                .build();
        int code = rawResponse.code();
        if (code < 200 || code >= 300) {
            try {
                ResponseBody bufferBody = Utils.buffer(rawBody);
                return Response.error(bufferBody, rawResponse);
            } finally {
                rawBody.close();
            }
        }
        if (code == 204 || code == 205) {
            Response.success(null, rawResponse);
        }

        ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
        T body = serviceMethod.toResponse(catchingBody);
        return Response.success(body,rawResponse);
    }

    static final class NoContentResponseBody extends ResponseBody {

        private final MediaType contentType;
        private final long contentLength;

        NoContentResponseBody(MediaType contentType, long contentLength) {
            this.contentType = contentType;
            this.contentLength = contentLength;
        }

        @Nullable
        @Override
        public MediaType contentType() {
            return contentType;
        }

        @Override
        public long contentLength() {
            return contentLength;
        }

        @Override
        public BufferedSource source() {
            return null;
        }
    }

    static final class ExceptionCatchingResponseBody extends ResponseBody {

        private final ResponseBody delegate;
        IOException thrownException;

        ExceptionCatchingResponseBody(ResponseBody responseBody) {
            this.delegate = responseBody;
        }

        @Nullable
        @Override
        public MediaType contentType() {
            return delegate.contentType();
        }

        @Override
        public long contentLength() {
            return delegate.contentLength();
        }

        @Override
        public BufferedSource source() {
            return Okio.buffer(new ForwardingSource(delegate.source()) {
                @Override
                public long read(Buffer sink, long byteCount) throws IOException {
                    try {
                        return super.read(sink, byteCount);
                    } catch (IOException e) {
                        thrownException = e;
                        throw e;
                    }
                }
            });
        }

        @Override
        public void close() {
            delegate.close();
        }

        void throwIfCaught() throws IOException {
            if (thrownException != null) {
                throw thrownException;
            }
        }
    }
}
